package br.com.usapre.faces;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import javax.faces.context.FacesContext;
import javax.faces.model.SelectItem;

import org.apache.commons.io.IOUtils;
import org.apache.myfaces.custom.fileupload.UploadedFile;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import br.com.business.BairroBusiness;
import br.com.business.CidadeBusiness;
import br.com.business.EnderecoBusiness;
import br.com.business.LogradouroBusiness;
import br.com.business.UfBusiness;
import br.com.data.Bairro;
import br.com.data.Cidade;
import br.com.data.Endereco;
import br.com.data.Logradouro;
import br.com.data.Uf;
import br.com.data.Usuario;
import br.com.databean.PerfilTO;
import br.com.login.business.UsuarioBusiness;
import br.com.login.faces.SessionUsaPreBean;
import br.com.utilidades.database.data.Criterio;
import br.com.utilidades.faces.SimpleMessageBean;
import constantes.CONSTANTES;

/**
 * <p>
 * PerfilBean
 * </p>
 * <p>
 * Descrição: Bean responsável por armazenar os métodos relacionados ao perfil
 * do usuário
 * </p>
 * <br>
 * <b>Empresa:</b> Nacional SA.
 * 
 * @author Waltenes Junior
 * @version 1.0
 */
@Component
@Scope("view")
public class PerfilBean extends SimpleMessageBean implements Serializable {

    private static final String DIRETORIO_IMAGEM_PERFIL = "imagemPerfil";

    private static final String DIRETORIO_RESOURCES = "resources";

    private static final long serialVersionUID = 145263299756049236L;

    private List<Uf> listaUF;
    private Uf uf;

    private List<Cidade> listaCidade;
    private Cidade cidade;

    private List<Bairro> listaBairro;
    private Bairro bairro;

    private List<SelectItem> listaSexo;
    private List<SelectItem> listaOperadora;

    private UfBusiness ufBusinessImpl;
    private CidadeBusiness cidadeBusinessImpl;
    private BairroBusiness bairroBusinessImpl;
    private EnderecoBusiness enderecoBusinesImpl;
    private SessionUsaPreBean sessionUsaPreBean;
    private UsuarioBusiness usuarioBusinessImpl;
    private LogradouroBusiness logradouroBusinessImpl;

    private List<Logradouro> listaLogradouro;
    private Logradouro logradouro;

    private String cepBusca;
    private String cidadeBusca;
    private String bairroBusca;
    private String logradouroBusca;

    private String numero;
    private String complemento;

    private Usuario usuario;

    private PerfilTO perfilTO;

    private UploadedFile arquivo;

    /**
     * Construtor
     */
    @Autowired
    public PerfilBean(SessionUsaPreBean sessionUsaPreBean, UsuarioBusiness usuarioBusinessImpl,
	    CidadeBusiness cidadeBusinessImpl, BairroBusiness bairroBusinessImpl, UfBusiness ufBusinessImpl,
	    EnderecoBusiness enderecoBusinesImpl, LogradouroBusiness logradouroBusinessImpl,
	    EnderecoBusiness enderecoBusiness) {

	this.sessionUsaPreBean = sessionUsaPreBean;

	this.usuarioBusinessImpl = usuarioBusinessImpl;

	this.bairroBusinessImpl = bairroBusinessImpl;
	this.cidadeBusinessImpl = cidadeBusinessImpl;
	this.ufBusinessImpl = ufBusinessImpl;
	this.enderecoBusinesImpl = enderecoBusinesImpl;
	this.logradouroBusinessImpl = logradouroBusinessImpl;
	this.enderecoBusinesImpl = enderecoBusinesImpl;

	this.usuario = sessionUsaPreBean.getUsuario();

	carregar();
    }

    /**
     * <p>
     * Método responsável por carregar os dados necessario para a tela inicial
     * <p>
     * 
     * @author Waltenes Junior
     */
    public void carregar() {

	this.listaBairro = new ArrayList<Bairro>();
	this.listaCidade = new ArrayList<Cidade>();

	this.listaUF = ufBusinessImpl.buscarPorNomeBusca("");

	this.listaSexo = new ArrayList<SelectItem>();
	this.listaSexo.add(new SelectItem("N", "Sexo"));
	this.listaSexo.add(new SelectItem("M", "Masculino"));
	this.listaSexo.add(new SelectItem("F", "Feminino"));

	this.listaOperadora = new ArrayList<SelectItem>();
	this.listaOperadora.add(new SelectItem("-1", "Selecione a Operadora"));
	this.listaOperadora.add(new SelectItem("Tim", "Tim"));
	this.listaOperadora.add(new SelectItem("Vivo", "Vivo"));
	this.listaOperadora.add(new SelectItem("Oi", "Oi"));
	this.listaOperadora.add(new SelectItem("Claro", "Claro"));
    }

    /**
     * <p>
     * Método responsável por buscar estado, cidade, bairro e logradouro por CEP
     * <p>
     * 
     * @author Waltenes Junior
     */
    public void buscarPorCep() {

	if (this.cepBusca.length() == 9) {

	    List<Criterio> criterios = new ArrayList<Criterio>();

	    Criterio c = new Criterio();
	    c.setTipo(Criterio.TIPO_INTEGER);
	    c.setValue(Integer.parseInt(this.cepBusca.replaceAll("-", "")));
	    c.setComparacao("=");
	    c.setCampo("cep");

	    criterios.add(c);

	    List<Logradouro> listaLogradouroBusca = this.logradouroBusinessImpl.buscarPorCriterios(criterios, "");

	    if (listaLogradouroBusca.size() > 0) {

		Logradouro logradouro = listaLogradouroBusca.get(0);

		this.listaCidade = new ArrayList<Cidade>();
		this.listaBairro = new ArrayList<Bairro>();
		this.listaLogradouro = new ArrayList<Logradouro>();

		this.setLogradouro(logradouro);
		this.listaLogradouro.add(logradouro);

		this.setBairro(logradouro.getBairro());
		this.listaBairro.add(logradouro.getBairro());

		this.setCidade(logradouro.getBairro().getCidade());
		this.listaCidade.add(logradouro.getBairro().getCidade());

		Uf ufTemp = logradouro.getBairro().getCidade().getUf();

		for (Uf uft : this.listaUF) {
		    if (uft.getId().equals(ufTemp.getId())) {
			this.usuario.setUf(uft);
		    }
		}
	    }
	} else {
	    String cepInvalido = super.getMensagem("MN_cep_invalido");
	    enviaMensagem(cepInvalido, cepInvalido);
	}
    }

    /**
     * <p>
     * Método responsável por buscar cidade
     * <p>
     * 
     * @author Waltenes Junior
     */
    public void buscarPorCidade() {

	if (this.usuario.getUf() == null) {
	    this.listaCidade = new ArrayList<Cidade>();
	    String selecioneEstado = super.getMensagem("MN_selecione_estado");
	    super.enviaMensagem(selecioneEstado, selecioneEstado);
	} else {

	    List<Criterio> criterios = new ArrayList<Criterio>();

	    Criterio c = new Criterio();
	    c.setTipo(Criterio.TIPO_STRING_CASE_SENSITIVE);
	    c.setValue("%" + this.cidadeBusca + "%");
	    c.setComparacao("like");
	    c.setCampo("nome");

	    criterios.add(c);

	    Criterio c1 = new Criterio();
	    c1.setTipo(Criterio.TIPO_INTEGER);
	    c1.setValue(this.usuario.getUf().getId());
	    c1.setComparacao("=");
	    c1.setCampo("uf.id");

	    criterios.add(c1);

	    this.listaCidade = this.cidadeBusinessImpl.buscarPorCriterios(criterios, "nome ASC");
	}
    }

    /**
     * <p>
     * Método responsável por buscar por bairro
     * <p>
     * 
     * @author Waltenes Junior
     */
    public void buscarPorBairro() {

	if (this.cidade == null) {
	    this.listaBairro = new ArrayList<Bairro>();
	    String selecioneCidade = super.getMensagem("MN_selecione_cidade");
	    super.enviaMensagem(selecioneCidade, selecioneCidade);
	} else {

	    List<Criterio> criterios = new ArrayList<Criterio>();

	    Criterio c = new Criterio();
	    c.setTipo(Criterio.TIPO_STRING_CASE_SENSITIVE);
	    c.setValue("%" + this.bairroBusca + "%");
	    c.setComparacao("like");
	    c.setCampo("nome");

	    criterios.add(c);

	    Criterio c1 = new Criterio();
	    c1.setTipo(Criterio.TIPO_INTEGER);
	    c1.setValue(this.cidade.getId());
	    c1.setComparacao("=");
	    c1.setCampo("cidade.id");

	    criterios.add(c1);

	    this.listaBairro = this.bairroBusinessImpl.buscarPorCriterios(criterios, "nome ASC");
	}
    }

    /**
     * <p>
     * Método responsável por buscar por logradouro
     * <p>
     * 
     * @author Waltenes Junior
     */
    public void buscaPorLogradouro() {

	if (this.bairro == null) {
	    this.listaLogradouro = new ArrayList<Logradouro>();
	    String selecioneEstado = super.getMensagem("MN_selecione_bairro");
	    enviaMensagem(selecioneEstado, selecioneEstado);
	} else {

	    List<Criterio> criterios = new ArrayList<Criterio>();

	    Criterio c = new Criterio();
	    c.setTipo(Criterio.TIPO_STRING_CASE_SENSITIVE);
	    c.setValue("%" + this.logradouroBusca + "%");
	    c.setComparacao("like");
	    c.setCampo("nome");

	    criterios.add(c);

	    Criterio c1 = new Criterio();
	    c1.setTipo(Criterio.TIPO_INTEGER);
	    c1.setValue(this.cidade.getId());
	    c1.setComparacao("=");
	    c1.setCampo("bairro.id");

	    criterios.add(c1);

	    this.listaLogradouro = this.logradouroBusinessImpl.buscarPorCriterios(criterios, "nome ASC");
	}
    }

    /**
     * <p>
     * Método responsável por abrir a tela de alteração de senha do perfil
     * <p>
     * 
     * @author Waltenes Junior
     */
    public void abrirAlterarSenha() {
	try {
	    FacesContext.getCurrentInstance().getExternalContext()
		    .redirect("/" + CONSTANTES.URLUSAPRE + "/alterarSenha.jsf");
	} catch (Exception ex) {
	    ex.printStackTrace();
	}
    }

    /**
     * <p>
     * Método responsável por abrir a tela de alteração da foto do perfil
     * <p>
     * 
     * @author Waltenes Junior
     */
    public void abrirAlterarFoto() {
	try {
	    FacesContext.getCurrentInstance().getExternalContext()
		    .redirect("/" + CONSTANTES.URLUSAPRE + "/alterarFotoPerfil.jsf");
	} catch (Exception ex) {
	    ex.printStackTrace();
	}
    }

    /**
     * <p>
     * Método responsável por abrir a tela de alteração de cadastro do perfil
     * <p>
     * 
     * @author Waltenes Junior
     */
    public void abrirAlterarCadastro() {
	try {
	    FacesContext.getCurrentInstance().getExternalContext()
		    .redirect("/" + CONSTANTES.URLUSAPRE + "/alterarCadastro.jsf");
	} catch (Exception ex) {
	    ex.printStackTrace();
	}
    }

    /**
     * <p>
     * Método responsável por atualizar o cadastro do usuário de acordo com os
     * dados digitado na tela
     * <p>
     * 
     * @author Waltenes Junior
     */
    public void atualizarCadastroUsuario() {

	if (this.logradouro != null) {
	    Endereco endereco = this.enderecoBusinesImpl.buscarEnderecoPorUsuario(this.usuario);
	    endereco.setComplemento(this.complemento);
	    endereco.setLogradouro(this.logradouro);
	    endereco.setNumero(this.numero);
	    this.enderecoBusinesImpl.atualizar(endereco);
	}

	this.usuarioBusinessImpl.atualizar(this.usuario);

	String msg = super.getMensagem("MN_cadastro_atualizado_sucesso");
	super.enviaMensagem(msg, msg);
    }

    /**
     * <p>
     * Método responsável por atualizar a senha do usuário
     * <p>
     * 
     * @author Waltenes Junior
     */
    public void atualizarSenhaAtual() {

	if (!this.perfilTO.getSenhaAtual().equals(this.usuario.getSenha())) {

	    String msg = super.getMensagem("MN_senha_diferente");
	    super.enviaMensagem(msg, msg);

	} else if (!this.perfilTO.getSenhaNova().equals(this.perfilTO.getConfirmacaoSenha())) {

	    String msg = super.getMensagem("MN_senha_diferente_confirmacao");
	    super.enviaMensagem(msg, msg);

	} else {

	    this.usuario.setSenha(this.perfilTO.getSenhaNova());
	    this.usuarioBusinessImpl.atualizar(this.usuario);

	    this.limparCampos();

	    String msg = super.getMensagem("MN_senha_alterada_sucesso");
	    super.enviaMensagem(msg, msg);
	}
    }

    /**
     * <p>
     * Método responsável por limpar os campos da tela de alteração de senha do
     * usuário
     * <p>
     * 
     * @author Waltenes Junior
     */
    private void limparCampos() {
	this.getPerfilTO().setConfirmacaoSenha(null);
	this.getPerfilTO().setSenhaAtual(null);
	this.getPerfilTO().setSenhaNova(null);
    }

    /**
     * <p>
     * Método responsável por atualizar cidades de acordo com o uf selecionado
     * <p>
     * 
     * @author Waltenes Junior
     */
    public void atualizaCidade() {

	if (this.usuario.getUf() != null) {

	    this.listaCidade = new ArrayList<Cidade>();

	    List<Criterio> criterios = new ArrayList<Criterio>();
	    Criterio crit = new Criterio();
	    crit.setCampo("uf.id");
	    crit.setComparacao(" = ");
	    crit.setValue(this.usuario.getUf().getId());
	    crit.setTipo(Criterio.TIPO_INTEGER);
	    criterios.add(crit);

	    this.listaCidade = this.cidadeBusinessImpl.buscarPorCriterios(criterios, " nome ");
	}
    }

    /**
     * <p>
     * Método responsável por atualizar bairro de acordo com a cidade
     * selecionada
     * <p>
     * 
     * @author Waltenes Junior
     */
    public void atualizaBairro() {

	if (this.cidade != null) {

	    this.listaBairro = new ArrayList<Bairro>();

	    List<Criterio> criterios = new ArrayList<Criterio>();
	    Criterio crit = new Criterio();
	    crit.setCampo("cidade.id");
	    crit.setComparacao(" = ");
	    crit.setValue(cidade.getId());
	    crit.setTipo(Criterio.TIPO_INTEGER);
	    criterios.add(crit);

	    this.listaBairro = this.bairroBusinessImpl.buscarPorCriterios(criterios, " nome ");
	}
    }

    /**
     * <p>
     * Método responsável por fazer upload da foto do perfil do usuário
     * <p>
     * 
     * @throws IOException
     * @author Waltenes Junior
     */
    public void upload() throws IOException {
	if (this.arquivo != null) {

	    String pathProjeto = FacesContext.getCurrentInstance().getExternalContext().getRealPath("/");
	    String caminhoFoto = DIRETORIO_RESOURCES + File.separator + DIRETORIO_IMAGEM_PERFIL + File.separator
		    + this.arquivo.getName();

	    OutputStream output = new FileOutputStream(new File(pathProjeto + caminhoFoto));

	    IOUtils.copy(this.arquivo.getInputStream(), output);

	    output.close();
	}
    }

    /**
     * Retorna o valor do atributo perfilTO.
     * 
     * @return perfilTO
     */
    public PerfilTO getPerfilTO() {
	if (this.perfilTO == null) {
	    this.perfilTO = new PerfilTO();
	}
	return perfilTO;
    }

    /**
     * Define o valor do atributo perfilTO.
     * 
     * @param perfilTO
     *            valor a ser atribuído
     */
    public void setPerfilTO(PerfilTO perfilTO) {
	this.perfilTO = perfilTO;
    }

    /**
     * Retorna o valor do atributo sessionUsaPreBean.
     * 
     * @return sessionUsaPreBean
     */
    public SessionUsaPreBean getSessionUsaPreBean() {
	return sessionUsaPreBean;
    }

    /**
     * Define o valor do atributo sessionUsaPreBean.
     * 
     * @param sessionUsaPreBean
     *            valor a ser atribuído
     */
    public void setSessionUsaPreBean(SessionUsaPreBean sessionUsaPreBean) {
	this.sessionUsaPreBean = sessionUsaPreBean;
    }

    /**
     * Retorna o valor do atributo usuarioBusinessImpl.
     * 
     * @return usuarioBusinessImpl
     */
    public UsuarioBusiness getUsuarioBusinessImpl() {
	return usuarioBusinessImpl;
    }

    /**
     * Define o valor do atributo usuarioBusinessImpl.
     * 
     * @param usuarioBusinessImpl
     *            valor a ser atribuído
     */
    public void setUsuarioBusinessImpl(UsuarioBusiness usuarioBusinessImpl) {
	this.usuarioBusinessImpl = usuarioBusinessImpl;
    }

    /**
     * Retorna o valor do atributo usuario.
     * 
     * @return usuario
     */
    public Usuario getUsuario() {
	return usuario;
    }

    /**
     * Define o valor do atributo usuario.
     * 
     * @param usuario
     *            valor a ser atribuído
     */
    public void setUsuario(Usuario usuario) {
	this.usuario = usuario;
    }

    /**
     * Retorna o valor do atributo listaUF.
     * 
     * @return listaUF
     */
    public List<Uf> getListaUF() {
	return listaUF;
    }

    /**
     * Define o valor do atributo listaUF.
     * 
     * @param listaUF
     *            valor a ser atribuído
     */
    public void setListaUF(List<Uf> listaUF) {
	this.listaUF = listaUF;
    }

    /**
     * Retorna o valor do atributo uf.
     * 
     * @return uf
     */
    public Uf getUf() {
	return uf;
    }

    /**
     * Define o valor do atributo uf.
     * 
     * @param uf
     *            valor a ser atribuído
     */
    public void setUf(Uf uf) {
	this.uf = uf;
    }

    /**
     * Retorna o valor do atributo listaCidade.
     * 
     * @return listaCidade
     */
    public List<Cidade> getListaCidade() {
	return listaCidade;
    }

    /**
     * Define o valor do atributo listaCidade.
     * 
     * @param listaCidade
     *            valor a ser atribuído
     */
    public void setListaCidade(List<Cidade> listaCidade) {
	this.listaCidade = listaCidade;
    }

    /**
     * Retorna o valor do atributo cidade.
     * 
     * @return cidade
     */
    public Cidade getCidade() {
	return cidade;
    }

    /**
     * Define o valor do atributo cidade.
     * 
     * @param cidade
     *            valor a ser atribuído
     */
    public void setCidade(Cidade cidade) {
	this.cidade = cidade;
    }

    /**
     * Retorna o valor do atributo listaBairro.
     * 
     * @return listaBairro
     */
    public List<Bairro> getListaBairro() {
	return listaBairro;
    }

    /**
     * Define o valor do atributo listaBairro.
     * 
     * @param listaBairro
     *            valor a ser atribuído
     */
    public void setListaBairro(List<Bairro> listaBairro) {
	this.listaBairro = listaBairro;
    }

    /**
     * Retorna o valor do atributo bairro.
     * 
     * @return bairro
     */
    public Bairro getBairro() {
	return bairro;
    }

    /**
     * Define o valor do atributo bairro.
     * 
     * @param bairro
     *            valor a ser atribuído
     */
    public void setBairro(Bairro bairro) {
	this.bairro = bairro;
    }

    /**
     * Retorna o valor do atributo listaSexo.
     * 
     * @return listaSexo
     */
    public List<SelectItem> getListaSexo() {
	return listaSexo;
    }

    /**
     * Define o valor do atributo listaSexo.
     * 
     * @param listaSexo
     *            valor a ser atribuído
     */
    public void setListaSexo(List<SelectItem> listaSexo) {
	this.listaSexo = listaSexo;
    }

    /**
     * Retorna o valor do atributo listaOperadora.
     * 
     * @return listaOperadora
     */
    public List<SelectItem> getListaOperadora() {
	return listaOperadora;
    }

    /**
     * Define o valor do atributo listaOperadora.
     * 
     * @param listaOperadora
     *            valor a ser atribuído
     */
    public void setListaOperadora(List<SelectItem> listaOperadora) {
	this.listaOperadora = listaOperadora;
    }

    /**
     * Retorna o valor do atributo cepBusca.
     * 
     * @return cepBusca
     */
    public String getCepBusca() {
	return cepBusca;
    }

    /**
     * Define o valor do atributo cepBusca.
     * 
     * @param cepBusca
     *            valor a ser atribuído
     */
    public void setCepBusca(String cepBusca) {
	this.cepBusca = cepBusca;
    }

    /**
     * Retorna o valor do atributo logradouroBusca.
     * 
     * @return logradouroBusca
     */
    public String getLogradouroBusca() {
	return logradouroBusca;
    }

    /**
     * Define o valor do atributo logradouroBusca.
     * 
     * @param logradouroBusca
     *            valor a ser atribuído
     */
    public void setLogradouroBusca(String logradouroBusca) {
	this.logradouroBusca = logradouroBusca;
    }

    /**
     * Retorna o valor do atributo listaLogradouro.
     * 
     * @return listaLogradouro
     */
    public List<Logradouro> getListaLogradouro() {
	return listaLogradouro;
    }

    /**
     * Define o valor do atributo listaLogradouro.
     * 
     * @param listaLogradouro
     *            valor a ser atribuído
     */
    public void setListaLogradouro(List<Logradouro> listaLogradouro) {
	this.listaLogradouro = listaLogradouro;
    }

    /**
     * Retorna o valor do atributo logradouro.
     * 
     * @return logradouro
     */
    public Logradouro getLogradouro() {
	return logradouro;
    }

    /**
     * Define o valor do atributo logradouro.
     * 
     * @param logradouro
     *            valor a ser atribuído
     */
    public void setLogradouro(Logradouro logradouro) {
	this.logradouro = logradouro;
    }

    /**
     * Retorna o valor do atributo cidadeBusca.
     * 
     * @return cidadeBusca
     */
    public String getCidadeBusca() {
	return cidadeBusca;
    }

    /**
     * Define o valor do atributo cidadeBusca.
     * 
     * @param cidadeBusca
     *            valor a ser atribuído
     */
    public void setCidadeBusca(String cidadeBusca) {
	this.cidadeBusca = cidadeBusca;
    }

    /**
     * Retorna o valor do atributo bairroBusca.
     * 
     * @return bairroBusca
     */
    public String getBairroBusca() {
	return bairroBusca;
    }

    /**
     * Define o valor do atributo bairroBusca.
     * 
     * @param bairroBusca
     *            valor a ser atribuído
     */
    public void setBairroBusca(String bairroBusca) {
	this.bairroBusca = bairroBusca;
    }

    /**
     * Retorna o valor do atributo numero.
     * 
     * @return numero
     */
    public String getNumero() {
	return numero;
    }

    /**
     * Define o valor do atributo numero.
     * 
     * @param numero
     *            valor a ser atribuído
     */
    public void setNumero(String numero) {
	this.numero = numero;
    }

    /**
     * Retorna o valor do atributo complemento.
     * 
     * @return complemento
     */
    public String getComplemento() {
	return complemento;
    }

    /**
     * Define o valor do atributo complemento.
     * 
     * @param complemento
     *            valor a ser atribuído
     */
    public void setComplemento(String complemento) {
	this.complemento = complemento;
    }

    /**
     * Retorna o valor do atributo file.
     * 
     * @return file
     */
    public UploadedFile getArquivo() {
	return arquivo;
    }

    /**
     * Define o valor do atributo file.
     * 
     * @param file
     *            valor a ser atribuído
     */
    public void setArquivo(UploadedFile arquivo) {
	this.arquivo = arquivo;
    }
}
